package com.anlogic.sdk.bootimage.internal.ui.composite;

import com.anlogic.sdk.bootimage.internal.ui.AutoCompleteProvider;
import com.anlogic.sdk.bootimage.internal.ui.common.BootgenCheckBoxField;
import com.anlogic.sdk.bootimage.internal.ui.common.BootgenComboViewer;
import com.anlogic.sdk.bootimage.internal.ui.common.BootgenTextField;
import com.anlogic.sdk.bootimage.internal.ui.common.IBaseBootgenField;
import com.anlogic.sdk.bootimage.internal.ui.common.IModifyListener;
import com.anlogic.sdk.bootimage.model.BootGenModel;
import com.anlogic.sdk.bootimage.model.Partition;
import com.anlogic.sdk.bootimage.model.options.AuthenticationType;
import com.anlogic.sdk.bootimage.model.options.EncryptionType;
import com.anlogic.sdk.bootimage.model.options.MpSoCKeyStore;
import com.anlogic.sdk.utils.StringUtils;
import com.anlogic.sdk.utils.UIUtils;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Layout;
import org.eclipse.swt.widgets.TabFolder;
import org.eclipse.swt.widgets.TabItem;

public class SecurityTabComposite extends AbsBootgenComposite {
	private static final String[] PK_EXT = new String[] { "*.txt;*.pem;*.pub", "*.*" };

	private static final String[] SK_EXT = new String[] { "*.txt;*.pem", "*.*" };

	private static final String[] PK_SIG_EXT = new String[] { "*.txt;*.sig" };

	private static final String[] KEY_FILE_EXT = new String[] { "*.nky" };

	private List<IBaseBootgenField> authFields = new ArrayList<>();

	private List<IBaseBootgenField> encryptFields = new ArrayList<>();

	private BootgenTextField ppkTextField;

	private BootgenTextField pskTextField;

	private BootgenTextField spkTextField;

	private BootgenTextField sskTextField;

	private BootgenTextField spkSignTextField;

	private BootgenTextField keyFileTextField;

	private BootgenComboViewer<MpSoCKeyStore> keyStoreField;

	private BootgenComboViewer<EncryptionType> encryptTypeField;
	
	private BootgenComboViewer<AuthenticationType> authSelectField;

	private BootgenTextField spkIdTextField;

	private BootgenCheckBoxField enableBHAuthField;
	
	private BootgenCheckBoxField enableGenAuthKeys;
	
	private BootgenCheckBoxField enableGenSpkSig;
	
	private PartitionTableComposite partitionTable;
	
	public SecurityTabComposite(Composite parent, BootGenModel model,
			AutoCompleteProvider.IAutoCompeletHelper autoCompleteHelper) {
		super(parent, 0, model, autoCompleteHelper);
		GridLayoutFactory.fillDefaults().margins(5, 5).applyTo(this);
		createUI(this);
	}

	private void createUI(Composite container) {
		setLayout((Layout) new GridLayout(5, false));
		TabFolder mainTabFolder = new TabFolder(this, 0);
		GridLayoutFactory.fillDefaults().applyTo((Composite) mainTabFolder);
		GridDataFactory.fillDefaults().grab(true, true).applyTo((Control) mainTabFolder);
		createAuthenticationSection(mainTabFolder);
		createSecuritySection(mainTabFolder);
	}

	private void createAuthenticationSection(TabFolder mainTabFolder) {
		TabItem authenticationItem = new TabItem(mainTabFolder, 0);
		authenticationItem.setText("Authentication");
		Composite authenticationComp = new Composite((Composite) mainTabFolder, 0);
		GridLayoutFactory.fillDefaults().numColumns(6).margins(5, 5).applyTo(authenticationComp);
		GridDataFactory.fillDefaults().grab(true, true).applyTo((Control) authenticationComp);
		authenticationItem.setControl((Control) authenticationComp);
		this.ppkTextField = new BootgenTextField(this);
		this.authFields.add(this.ppkTextField);
		this.ppkTextField.createOpenBrowseField(authenticationComp, "PPK", "bootgen_files_selection", PK_EXT);
		this.ppkTextField.setDefaultGridData(1);
		addAutoCompletionCapability(this.ppkTextField.getControl());
		this.pskTextField = new BootgenTextField(this);
		this.authFields.add(this.pskTextField);
		this.pskTextField.createOpenBrowseField(authenticationComp, "PSK", "bootgen_files_selection", SK_EXT);
		this.pskTextField.setDefaultGridData(1);
		addAutoCompletionCapability(this.pskTextField.getControl());
		this.spkTextField = new BootgenTextField(this);
		this.authFields.add(this.spkTextField);
		this.spkTextField.createOpenBrowseField(authenticationComp, "SPK", "bootgen_files_selection", PK_EXT);
		this.spkTextField.setDefaultGridData(1);
		addAutoCompletionCapability(this.spkTextField.getControl());
		this.sskTextField = new BootgenTextField(this);
		this.authFields.add(this.sskTextField);
		this.sskTextField.createOpenBrowseField(authenticationComp, "SSK", "bootgen_files_selection", SK_EXT);
		this.sskTextField.setDefaultGridData(1);
		addAutoCompletionCapability(this.sskTextField.getControl());
		this.spkSignTextField = new BootgenTextField(this);
		this.authFields.add(this.spkSignTextField);
		this.spkSignTextField.createOpenBrowseField(authenticationComp, "SPK Signature", "bootgen_files_selection",
				PK_SIG_EXT);
		this.spkSignTextField.setDefaultGridData(1);
		addAutoCompletionCapability(this.spkSignTextField.getControl());
		createAuthSection(authenticationComp);
	}
	
	private void createAuthSection(Composite authenticationComp) {
		Composite authComposite = new Composite(authenticationComp, 0);
		GridDataFactory.fillDefaults().grab(true, false).span(6, 1).applyTo((Control) authComposite);
		GridLayoutFactory.fillDefaults().numColumns(4).applyTo(authComposite);
		Point hint = new Point(UIUtils.convertWidthInCharsToPixels((Control) authComposite, 20), -1);
		this.authSelectField = new BootgenComboViewer<>(this);
		this.authFields.add(this.authSelectField);
		this.authSelectField.createField(authComposite, "Authentication Type: ", (Object[]) AuthenticationType.values());
		GridDataFactory.swtDefaults().hint(hint).applyTo((Control) this.authSelectField.getControl());
		this.spkIdTextField = new BootgenTextField(this);
		this.authFields.add(this.spkIdTextField);
		this.spkIdTextField.createField(authComposite, "SPK ID: ");
		GridDataFactory.swtDefaults().hint(hint).applyTo((Control) this.spkIdTextField.getControl());
		this.enableBHAuthField = new BootgenCheckBoxField(this);
		this.authFields.add(this.enableBHAuthField);
		this.enableBHAuthField.createField(authComposite, "Use BH Auth");
		GridDataFactory.swtDefaults().span(1, 1).applyTo((Control) this.enableBHAuthField.getControl());
		this.enableGenAuthKeys = new BootgenCheckBoxField(this);
		this.authFields.add(this.enableGenAuthKeys);
		this.enableGenAuthKeys.createField(authComposite, "Generate Auth Keys");
		GridDataFactory.swtDefaults().span(1, 1).applyTo((Control) this.enableGenAuthKeys.getControl());
		this.enableGenSpkSig = new BootgenCheckBoxField(this);
		this.authFields.add(this.enableGenSpkSig);
		this.enableGenSpkSig.createField(authComposite, "Generate Spk Signature");
		GridDataFactory.swtDefaults().span(1, 1).applyTo((Control) this.enableGenSpkSig.getControl());
	}

	private void createSecuritySection(TabFolder mainTabFolder) {
		TabItem encryptionItem = new TabItem(mainTabFolder, 0);
		encryptionItem.setText("Encryption");
		Composite encryptionComposite = new Composite((Composite) mainTabFolder, 0);
		GridLayoutFactory.fillDefaults().numColumns(3).margins(5, 5).applyTo(encryptionComposite);
		GridDataFactory.fillDefaults().grab(true, true).applyTo((Control) encryptionComposite);
		encryptionItem.setControl((Control) encryptionComposite);
		this.keyFileTextField = new BootgenTextField(this);
		this.encryptFields.add(this.keyFileTextField);
		this.keyFileTextField.createOpenBrowseField(encryptionComposite, "Key file", "bootgen_files_selection",
				KEY_FILE_EXT);
		this.keyFileTextField.setDefaultGridData(1);
		addAutoCompletionCapability(this.keyFileTextField.getControl());
		createEncryptSection(encryptionComposite);
	}

	private void createEncryptSection(Composite encryptionComposite) {
		Composite zmEncryptComposite = new Composite(encryptionComposite, 0);
		GridDataFactory.fillDefaults().grab(true, false).span(3, 1).applyTo((Control) zmEncryptComposite);
		GridLayoutFactory.fillDefaults().numColumns(4).applyTo(zmEncryptComposite);
		this.keyStoreField = new BootgenComboViewer<>(this);
		this.encryptFields.add(this.keyStoreField);
		this.keyStoreField.createField(zmEncryptComposite, "Key store: ", (Object[]) MpSoCKeyStore.values());
		GridDataFactory.swtDefaults().applyTo((Control) this.keyStoreField.getControl());
		
		this.encryptTypeField = new BootgenComboViewer<>(this);
		this.encryptFields.add(this.encryptTypeField);
		this.encryptTypeField.createField(zmEncryptComposite, "Encryption Type: ", (Object[]) EncryptionType.values());
		GridDataFactory.swtDefaults().applyTo((Control) this.encryptTypeField.getControl());
	}

	public void loadUI() {
		loadAuthUI();
		loadEncryptUI();
	}

	private void loadAuthUI() {
		this.ppkTextField.setSelection(this.model.getPpkFile());
		this.pskTextField.setSelection(this.model.getPskFile());
		this.spkTextField.setSelection(this.model.getSpkFile());
		this.sskTextField.setSelection(this.model.getSskFile());
		this.spkSignTextField.setSelection(this.model.getSpkSignFile());
		this.authSelectField.setSelection(this.model.getAuthType());
		this.spkIdTextField.setSelection(this.model.getSpkId());
		this.enableBHAuthField.setSelection(Boolean.valueOf(this.model.isBHAuth()));
		this.enableGenAuthKeys.setSelection(Boolean.valueOf(this.model.isGenAuthKeys()));
		this.enableGenSpkSig.setSelection(Boolean.valueOf(this.model.isGenSpkSig()));
		updateAuthenticationState();
	}

	private void loadEncryptUI() {
		this.keyFileTextField.setSelection(this.model.getKeyFile());
		this.keyStoreField.setSelection(this.model.getMpSoCKeyStore());
		this.encryptTypeField.setSelection(this.model.getEncryptionType());
		updateEncryptionState();
	}

	public void addListeners() {
		addAuthSectionListeners();
		addEncryptSectionListeners();
	}

	private void addAuthSectionListeners() {
		this.ppkTextField.setModifyListener(new IModifyListener<String>() {
			public void valueChanged(String newValue) {
				model.setPpkFile(newValue);
				updateAuthenticationState();
				markEditedAndValidate();
			}
		});
		this.pskTextField.setModifyListener(new IModifyListener<String>() {
			public void valueChanged(String newValue) {
				model.setPskFile(newValue);
				updateAuthenticationState();
				markEditedAndValidate();
			}
		});
		this.spkTextField.setModifyListener(new IModifyListener<String>() {
			public void valueChanged(String newValue) {
				model.setSpkFile(newValue);
				updateAuthenticationState();
				markEditedAndValidate();
			}
		});
		this.sskTextField.setModifyListener(new IModifyListener<String>() {
			public void valueChanged(String newValue) {
				model.setSskFile(newValue);
				updateAuthenticationState();
				markEditedAndValidate();
			}
		});
		this.spkSignTextField.setModifyListener(new IModifyListener<String>() {
			public void valueChanged(String newValue) {
				model.setSpkSignFile(newValue);
				updateAuthenticationState();
				markEditedAndValidate();
			}
		});
		this.enableGenAuthKeys.setModifyListener(new IModifyListener<Boolean>() {
			public void valueChanged(Boolean newValue) {
				model.setGenAuthKeys(newValue.booleanValue());
				updateAuthenticationState();
				markEditedAndValidate();
			}
		});
		this.enableGenSpkSig.setModifyListener(new IModifyListener<Boolean>() {
			public void valueChanged(Boolean newValue) {
				model.setGenSpkSig(newValue.booleanValue());
				updateAuthenticationState();
				markEditedAndValidate();
			}
		});
		addMpSoCAuthSectionListeners();
	}

	private void addMpSoCAuthSectionListeners() {
		this.authSelectField.setModifyListener(new IModifyListener<AuthenticationType>() {
			public void valueChanged(AuthenticationType newValue) {
				model.setAuthType(newValue);
				updateAuthenticationState();
				markEditedAndValidate();
				updatePartitionAuthentication();
				partitionTable.refreshUI();
			}
		});
		this.spkIdTextField.setModifyListener(new IModifyListener<String>() {
			public void valueChanged(String newValue) {
				model.setSpkId(newValue);
				updateAuthenticationState();
				markEditedAndValidate();
			}
		});
		this.enableBHAuthField.setModifyListener(new IModifyListener<Boolean>() {
			public void valueChanged(Boolean newValue) {
				model.setBHAuth(newValue.booleanValue());
				updateAuthenticationState();
				markEditedAndValidate();
			}
		});
	}

	private void addEncryptSectionListeners() {
		this.keyFileTextField.setModifyListener(new IModifyListener<String>() {
			public void valueChanged(String newValue) {
				model.setKeyFile(newValue);
				if (!model.getEncryptionType().equals(EncryptionType.NONE) && newValue.isEmpty()) {
					String keyFile = model.getOutputDir() + "/" + model.getEncryptionType().getName() + ".nky";
					model.setKeyFile(keyFile);
				}
				updateEncryptionState();
				markEditedAndValidate();
				updatePartitionEncryptionKeyfile();
				partitionTable.refreshUI();
			}
		});
		addMpSoCEncryptSectionListeners();
	}

	private void addMpSoCEncryptSectionListeners() {
		this.keyStoreField.setModifyListener(new IModifyListener<MpSoCKeyStore>() {
			public void valueChanged(MpSoCKeyStore newValue) {
				model.setMpSoCKeyStore(newValue);
				updateEncryptionState();
				markEditedAndValidate();
			}
		});
		this.encryptTypeField.setModifyListener(new IModifyListener<EncryptionType>() {
			public void valueChanged(EncryptionType newValue) {
				model.setEncryptionType(newValue);
				if (!newValue.equals(EncryptionType.NONE) && keyFileTextField.getSelection().isEmpty()) {
					String keyFile = model.getOutputDir() + "/" + model.getEncryptionType().getName() + ".nky";
					model.setKeyFile(keyFile);
				}
				updateEncryptionState();
				markEditedAndValidate();
				updatePartitionEncryption();
				partitionTable.refreshUI();
			}
		});
	}
	
	private void updateAuthenticationState() {
		if (model.getAuthType() != AuthenticationType.NONE) {
			model.setUseAuthentication(true);
			return;
		}
		if (!model.getPpkFile().isEmpty()) {
			model.setUseAuthentication(true);
			return;
		}
		if (!model.getPskFile().isEmpty()) {
			model.setUseAuthentication(true);
			return;
		}
		if (!model.getSpkFile().isEmpty()) {
			model.setUseAuthentication(true);
			return;
		}
		if (!model.getSskFile().isEmpty()) {
			model.setUseAuthentication(true);
			return;
		}
		if (!model.getSpkSignFile().isEmpty()) {
			model.setUseAuthentication(true);
			return;
		}
		if (model.isBHAuth()) {
			model.setUseAuthentication(true);
			return;
		}
		if (!model.getSpkId().isEmpty()) {
			model.setUseAuthentication(true);
			return;
		}
		model.setUseAuthentication(false);
	}
	
	private void updateEncryptionState() {
		if (model.getEncryptionType() != EncryptionType.NONE) {
			model.setUseEncryption(true);
			return;
		}
		model.setUseEncryption(false);
	}
	
	private void updatePartitionAuthentication() {
		for (Partition partition : model.getPartitions()) {
			partition.setAuthenticationType(AuthenticationType.NONE);
		}
	}
	
	private void updatePartitionEncryptionKeyfile() {
		String keyFile = model.getKeyFile();
		for (Partition partition : model.getPartitions()) {
			if (StringUtils.isNotEmpty(keyFile)) {
				if (model.getEncryptionType().equals(EncryptionType.AES)) {
					partition.setAESKeyFile(keyFile);
				} else if (model.getEncryptionType().equals(EncryptionType.SM4)) {
					partition.setSM4KeyFile(keyFile);
				}
			}
		}
	}
	
	private void updatePartitionEncryption() {
		String keyFile = model.getKeyFile();
		for (Partition partition : model.getPartitions()) {
			if (partition.isFsbl()) {
				partition.setEncryptionType(model.getEncryptionType());
			} else {
				partition.setEncryptionType(EncryptionType.NONE);
			}
			if (StringUtils.isNotEmpty(keyFile)) {
				if (model.getEncryptionType().equals(EncryptionType.AES)) {
					partition.setAESKeyFile(keyFile);
				} else if (model.getEncryptionType().equals(EncryptionType.SM4)) {
					partition.setSM4KeyFile(keyFile);
				}
			}
		}
	}
	
	public void setPartitionTable(PartitionTableComposite partitionTable) {
		this.partitionTable = partitionTable;
	}
}
